package com.stars.easyms.monitor.handler;

import com.stars.easyms.monitor.MonitorRequestHandler;
import com.stars.easyms.base.util.StringFormatUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>className: EasyMsRequestMappingHandlerMapping</p>
 * <p>description: 基础RequestMappingHandler注册类</p>
 *
 * @author guoguifang
 * @version 1.2.1
 * @date 2019-03-20 19:51
 */
public class EasyMsRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

    private final Map<String, HandlerMethod> handlerMethodMap = new ConcurrentHashMap<>(64);

    private final RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();

    private static final Method EASY_MS_MONITOR_REQUEST_HANDLER_TO_INDEX = ReflectionUtils.findMethod(MonitorRequestHandler.class, "toIndex", HttpServletRequest.class, HttpServletResponse.class);

    private static final Method EASY_MS_MONITOR_REQUEST_HANDLER_GET_MONITOR_INFO = ReflectionUtils.findMethod(MonitorRequestHandler.class, "getMonitorInfo");

    public EasyMsRequestMappingHandlerMapping(ApplicationContext context) {
        this.setOrder(-100);
        this.setUseSuffixPatternMatch(false);
        this.setApplicationContext(context);
        super.afterPropertiesSet();
    }

    public void detectHandlerMethods(EasyMsRequestHandler easyMsRequestHandler) {
        super.detectHandlerMethods(easyMsRequestHandler);
    }

    public void detectHandlerMethods(MonitorRequestHandler easyMsMonitorRequestHandler) {
        if (EASY_MS_MONITOR_REQUEST_HANDLER_TO_INDEX != null && EASY_MS_MONITOR_REQUEST_HANDLER_GET_MONITOR_INFO != null) {
            registerHandlerMethod(easyMsMonitorRequestHandler, EASY_MS_MONITOR_REQUEST_HANDLER_TO_INDEX,
                    RequestMappingInfo.paths(easyMsMonitorRequestHandler.getIndexUrl()).methods(RequestMethod.GET).options(config).build());
            registerHandlerMethod(easyMsMonitorRequestHandler, EASY_MS_MONITOR_REQUEST_HANDLER_GET_MONITOR_INFO,
                    RequestMappingInfo.paths("easy-ms/" + easyMsMonitorRequestHandler.getIndexUrl() + "/getInfo").methods(RequestMethod.POST).options(config).build());
        }
        super.detectHandlerMethods(easyMsMonitorRequestHandler);
    }

    @Override
    protected void registerHandlerMethod(@NonNull Object handler, @NonNull Method method, @NonNull RequestMappingInfo mapping) {
        if (handler instanceof EasyMsRequestHandler) {
            HandlerMethod handlerMethod = createHandlerMethod(handler, method);
            Set<String> defaultPatterns = mapping.getPatternsCondition().getPatterns();
            List<String> patterns = new ArrayList<>(defaultPatterns);
            for (int i = 0; i < patterns.size(); ++i) {
                String pattern = StringFormatUtil.formatRequestMappingPath(patterns.get(i));
                patterns.set(i, pattern);
                handlerMethodMap.put(pattern, handlerMethod);
            }
            super.registerHandlerMethod(handler, method, this.withNewPatterns(mapping, patterns.toArray(new String[0])));
        }
    }

    @Override
    @Nullable
    protected HandlerMethod lookupHandlerMethod(@NonNull String lookupPath, @NonNull HttpServletRequest request) throws Exception {
        if (handlerMethodMap.size() > 0) {
            String formattingPath = StringFormatUtil.formatRequestMappingPath(lookupPath);
            HandlerMethod handlerMethod = null;
            if ("/easy-ms/getInfo".equals(formattingPath)) {
                String url = StringFormatUtil.formatRequestMappingPath(request.getParameter("url"));
                int index;
                while ((index = url.indexOf('/')) != -1 && handlerMethod == null) {
                    url = url.substring(index + 1);
                    formattingPath = StringFormatUtil.formatRequestMappingPath("easy-ms/" + url + "/getInfo");
                    handlerMethod = handlerMethodMap.get(formattingPath);
                }
            } else {
                handlerMethod = handlerMethodMap.get(formattingPath);
            }
            if (handlerMethod != null) {
                return handlerMethod;
            }
        }
        return super.lookupHandlerMethod(lookupPath, request);
    }

    private RequestMappingInfo withNewPatterns(RequestMappingInfo mapping, String[] patternStrings) {
        PatternsRequestCondition patterns = new PatternsRequestCondition(patternStrings, null, null, this.useSuffixPatternMatch(), this.useTrailingSlashMatch(), null);
        return new RequestMappingInfo(patterns, mapping.getMethodsCondition(), mapping.getParamsCondition(), mapping.getHeadersCondition(), mapping.getConsumesCondition(), mapping.getProducesCondition(), mapping.getCustomCondition());
    }
}